(*
	vidDrvrP6000A(cmd,parameters) -- Driver for the Pioneer 6000A & 6010A videodisc player. The cmd parameter specifies the
		function to be performed (p1 is the first parameter after cmd; p2 is the second, etc.):

					Command		Function
					---------		--------
					chapter			Return the chapter currently being displayed.
					control			Execute a series of control functions. Each addition parameter is a keyword to be
										executed. If the keyword doesn't make sense here, pass it on to configureSPort.
										The following keywords are understood by the video driver:
													Keyword						Function
													--------						--------
													init or reset					Reset the player, configure the serial port (the baud rate is set to
																						the highest available for the selected player).
													eject or reject				Eject the disc from the player.
													audioOff						Turn off both audio channels.
													audio1On						Turn on audio channel 1.
													audio2On						Turn on audio channel 2.
													stereoOn						Turn on both audio channels.
													pictureOn/pictureOff	Turn on/off the picture.
													framesOn/framesOff	Turn on/off the display of frame numbers.
													frameMode					Set player to frame number mode.
													chapterMode					Set player to chapter number mode.
													timeMode						Set player to time mode.
													defaultComm					Set default communications settings for this player.
					extended		Execute a function specific to the player. This player doesn't have any (yet).
					fps				Set the frames per second for the next playVideo command to p1, which can be a number or
										one of "slowest", "slower", "slow", "normal", "fast", "faster", "fastest".
					frame			Return the current frame number.
					name			Return the long name of the player.
					play				Start a sequence playing, from p1 to p2, which are frame numbers, chapter numbers, or times,
										depending upon the mode.
					scan				Scan forward or backward, depending upong whether p1 is "forward" or "backward".
					search			Search to frame, chapter, or time p1.
					sendCmd		Send command p1 to the player and wait for an acknowlege.
					speeds			Return the frames per second speeds allowed with this player.
					status			Return the status of the play, which is a comma-separated list containing:
														Keyword											Meaning
														--------											--------
														doorOpen or park or still or play		State of player.
														CLV or CAV										Type of disc.
														disc12inch or disc8inch						Size of disc being played.
														side1 or side2									Side of disc being played.
					step				Step p1 frames forward (or backward if it's negative), and do it p2 times.
					time				Return the time of the frame currently being displayed, in 1/60ths of a second since the start
										of the disc.
					version			Return the version of this player driver.

	To compile and link this file using Macintosh Programmer's Workshop,

		pascal -w vidDrvrP6000A.p
		link -m ENTRYPOINT -o HyperCommands -rt XFCN=8031 -sn Main=vidDrvrP6000A ¶
			vidDrvrP6000A.p.o "{MPW}"Libraries:interface.o "{MPW}"PLibraries:PasLib.o

	Copyright © 1988 Apple Computer, Inc.

	2/88 - Initial coding by Harry R. Chesley.
*)

{$R-}

{$S vidDrvrP6000A } 	{ Segment name must be the same as the command name. }

unit DummyUnit;

interface

uses MemTypes, QuickDraw, OSIntf, ToolIntf, HyperXCmd;

procedure EntryPoint(paramPtr: XCmdPtr);
	
implementation

type

Str31 = String[31];

procedure vidDrvrP6000A(paramPtr: XCmdPtr); forward;

procedure EntryPoint(paramPtr: XCmdPtr);

	begin
		vidDrvrP6000A(paramPtr);
	end;

procedure vidDrvrP6000A(paramPtr: XCmdPtr);

	var returnValue: str255;
		pCount: integer;
		p1, p2: str255;
		str: str255;
		i: integer;
		inChapterMode: boolean;

	{$I XCmdGlue.inc}

	procedure Fail(errMsg: Str255); { set theResult and quit }
		begin
			paramPtr^.returnValue := PasToZero(errMsg);
			exit(vidDrvrP6000A);
		end;

	{$I VideoUtil.inc}

	procedure sendCmd(theCommand: str255);
		{ Send a command to the player. Note that this player driver, unlike most, does not
			wait for a positive ack after sending a command. }

		var i: integer;
			theChar: char;
			doingDigits: boolean;
			str: str255;

		begin
			SendCardMessage('sendSPort "@"');
			doingDigits := false;
			for i := 1 to length(theCommand) do
				begin
					theChar := theCommand[i];
					if doingDigits then
						begin
							if (theChar >= '0') and (theChar <= '9') then
								begin
									case theChar of
										'0': str := '3';
										'1': str := '0';
										'2': str := '8';
										'3': str := '4';
										'4': str := '2';
										'5': str := 'A';
										'6': str := '6';
										'7': str := '1';
										'8': str := '9';
										'9': str := '5';
										end;
									SendCardMessage(Concat('sendSPort "',str,'F"'));
								end
							else doingDigits := false;
						end
					else
						begin
							if theChar = '.' then doingDigits := true
							else SendCardMessage(Concat('sendSPort "',Copy(theCommand,i,1),'"'));
						end;
				end;
		end;

	function chapter: str255;
		{ Return the current chapter. }

		var str: str255;
			endTick: longInt;
			charCount: longInt;

		begin
			{ Ask for the chapter number in binary. }
			sendCmd('E7D2');
			{ Get it or time out. }
			endTick := TickCount+120;
			repeat charCount := StrToLong(Evalstr('charsAvailable()'));
			until (charCount >= 1) or (TickCount > endTick);
			{ If we got something... }
			chapter := 'noAnswer';
			if charCount >= 1 then
				begin
					{ Compute chapter number. }
					str := EvalStr('recvChars(1)');
					if length(str) <> 0 then chapter := LongToStr(ord(str[1]))
					else chapter := '0';
				end;
		end;

	function status: str255;
		{ Return the current status. }

		var str, str2: str255;
			endTick: longInt;
			charCount: longInt;
			theResult: str255;

		begin
			{ Clear out any pending input. }
			EvalAndDispose('recvUpTo(empty,0,empty)');
	
			{ Ask for the status in binary. }
			sendCmd('E7D5');
			{ Get it or time out. }
			endTick := TickCount+120;
			repeat charCount := StrToLong(Evalstr('charsAvailable()'));
			until (charCount >= 3) or (TickCount > endTick);
			{ If we got something... }
			if charCount >= 3 then
				begin
					{ str := character one. }
					str := EvalStr('recvChars(1)');
					if length(str) = 0 then
						begin
							str[0] := chr(1); str[1] := chr(0);
						end;
					{ str2 := character two. }
					str2 := EvalStr('recvChars(1)');
					if length(str2) = 0 then
						begin
							str2[0] := chr(1); str2[1] := chr(0);
						end;
					{ Dump char three. }
					EvalAndDispose('recvChars(1)');
					{ Compute the status. }
					if (ord(str[1]) mod 128) in [104,120,136] then theResult := 'park'
					else if (ord(str[1]) mod 4) = 1 then theResult := 'still'
					else theResult := 'play';
					if (ord(str2[1]) mod 2) = 1 then theResult := Concat(theResult,',CLV')
					else theResult := Concat(theResult,',CAV');

					{ Now get the other status info. }
					EvalAndDispose('recvUpTo(empty,0,empty)');
			
					{ Ask for the status in binary. }
					sendCmd('E791');
					{ Get it or time out. }
					endTick := TickCount+120;
					repeat charCount := StrToLong(Evalstr('charsAvailable()'));
					until (charCount >= 2) or (TickCount > endTick);
					{ If we got something... }
					if charCount >= 2 then
						begin
							{ str := character one. }
							str := EvalStr('recvChars(1)');
							str := EvalStr('recvChars(1)');
							if length(str) = 0 then
								begin
									str[0] := chr(1); str[1] := chr(0);
								end;
							{ Compute the status. }
							if BitAnd(ord(str[1]),$00080) <> 0 then theResult := Concat(theResult,',disc8inch')
							else theResult := Concat(theResult,',disc12inch');
							if BitAnd(ord(str[1]),$00040) <> 0 then theResult := Concat(theResult,',side2')
							else theResult := Concat(theResult,',side1');
						end;
					status := theResult;
				end
			else status := 'noAnswer';
		end;

	procedure search(var toFrame: str255; blankSearch: boolean);
		{ Search to the specified frame, using blanking if blankSearch is true. }

		begin
			if blankSearch then sendCmd('1C');
			sendCmd(Concat('.',toFrame,'.F7'));
			while StringEqual(Copy(status,1,4),'play') do ;
			if blankSearch then sendCmd('1B');
		end;

	procedure stop;
		{ Stop the player. }

		begin
			sendCmd('FB');
		end;

	procedure control(var keywd: str255);
		{ Handle a control command. }

		type
			audioModes = (off,oneOn,twoOn,stereo);

		var numberOfParms: integer;
			i: integer;
			parm: str255;

		procedure ejectPlayer;
			{ Eject the disc. }
	
			begin
				{ Send the appropriate eject command. }
				sendCmd('F9');
			end;
	
		procedure audio(onOff: audioModes);
			{ Set the audio mode. }
	
			begin
				{ Send the appropriate audio channel command. }
				case onOff of
					off:
						begin
							sendCmd('AA');
							sendCmd('A5');
						end;
					oneOn:
						begin
							sendCmd('AB');
							sendCmd('A5');
						end;
					twoOn:
						begin
							sendCmd('AA');
							sendCmd('A7');
						end;
					stereo:
						begin
							sendCmd('AB');
							sendCmd('A7');
						end;
					end;
			end;
		
		procedure picture(onOff: boolean);
			{ Turn the picture on or off. }
	
			begin
				{ Send the appropriate picture command. }
				if onOff then sendCmd('1B')
				else sendCmd('1C');
			end;
	
		procedure frames(onOff: boolean);
			{ Turn the frame display on or off. }
	
			begin
				{ Send the appropriate frame display command. }
				if onOff then sendCmd('E00FF1')
				else sendCmd('E13FF1');
			end;
	
		procedure defaultComm;
			{ Set the default communications settings. }

			begin
				{ Set the port configuration. }
				SendCardMessage('configureSPort baud9600,echoOff,editOff,linefeedOff,stripOff');
			end;

		procedure setMode(theMode: str255);
			{ Set the frame/chapter/time mode. }
	
			begin
				SetStrGlobal('videoMode',theMode);
				if StringEqual(theMode,'chapterMode') then sendCmd('8C')
				else if StringEqual(theMode,'timeMode') then sendCmd('8D')
				else sendCmd('8E')
			end;
	
		procedure initPlayer;
			{ Initialize the player. }
	
			var str: str255;
				endTick: longInt;
	
			begin
				{ Empty out the globals. }
				SetStrGlobal('videoMode','');
				SetStrGlobal('blankNextVideo','');
				SetStrGlobal('videoSpeed','');
	
				{ Send the reset command for this player. }
				sendCmd('FD');

				{ Wait for it to spin up. }
				endTick := TickCount+3600;
				repeat
					sendCmd('E4D4');
					str := EvalStr('recvUpTo(empty,20,empty)');
					if length(str) >= 2 then
						if str[2] = '4' then leave;
				until TickCount > endTick;

				{ Force it to stop. }
				sendCmd('BF');
				stop;

				{ Reset the player to known defaults. }
				setMode('');
				audio(stereo);
				frames(false);
				picture(true);
			end;
	
		begin
			if StringEqual(keywd,'init') or StringEqual(keywd,'reset') then initPlayer
			else if StringEqual(keywd,'eject') or StringEqual(keywd,'reject') then ejectPlayer
			else if StringEqual(keywd,'audioOff') then audio(off)
			else if StringEqual(keywd,'audio1On') then audio(oneOn)
			else if StringEqual(keywd,'audio2On') then audio(twoOn)
			else if StringEqual(keywd,'stereoOn') then audio(stereo)
			else if StringEqual(keywd,'pictureOn') then picture(true)
			else if StringEqual(keywd,'pictureOff') then picture(false)
			else if StringEqual(keywd,'framesOn') then frames(true)
			else if StringEqual(keywd,'framesOff') then frames(false)
			else if StringEqual(keywd,'defaultComm') then defaultComm
			else if StringEqual(keywd,'frameMode') then setMode('')
			else if StringEqual(keywd,'chapterMode') then setMode('chapterMode')
			else if StringEqual(keywd,'timeMode') then setMode('timeMode')
			else SendCardMessage(Concat('configureSPort ',keywd));
		end;

	function extended(var keywd: str255): str255;
		{ Execute an extended function or command. }

		begin
			{ No extended command for this player. }
		end;

	procedure fps(var fpsKeywd: str255);
		{ Set the frame per second. }

		var speed: str255;

		begin
			speed := '';

			{ Default speed settings are fine, except: }
			if StringEqual(fpsKeywd,'fastest') then speed := '90';

			{ Set it. }
			if speed <> '' then SetStrGlobal('videoSpeed',speed);
		end;

	function frame: str255;
		{ Return the current frame number. }

		var str, str2: str255;
			endTick: longInt;
			charCount: longInt;

		begin
			{ Ask for the frame number in binary. }
			sendCmd('E7D3');
			{ Get it or time out. }
			endTick := TickCount+120;
			repeat charCount := StrToLong(Evalstr('charsAvailable()'));
			until (charCount >= 2) or (TickCount > endTick);
			{ If we got something... }
			if charCount >= 2 then
				begin
					{ str := character one. }
					str := EvalStr('recvChars(1)');
					if length(str) = 0 then
						begin
							str[0] := chr(1); str[1] := chr(0);
						end;
					{ str2 := character two. }
					str2 := EvalStr('recvChars(1)');
					if length(str2) = 0 then
						begin
							str2[0] := chr(1); str2[1] := chr(0);
						end;
					{ Compute the frame number. }
					frame := LongToStr(BitOr(BitShift(ord(str[1]),8),ord(str2[1])))
				end
			else frame := 'noAnswer';
		end;

	function name: str255;
		{ Return the long name of the player. }

		begin
			name := 'Pioneer 6000A,Pioneer 6010A';
		end;

	procedure play(var firstFrame,lastFrame,speed: str255; blankSearch, chapterMode: boolean);
		{ Play a segment. }

		var lastFrameNum: longInt;
			startHere: boolean;
			playToLast: boolean;
			fpsNum: longInt;
			rev: boolean;

		begin
			{ Figure out if we're playing to special (non-numeric markers). }
			startHere := StringEqual(firstFrame,'here');
			playToLast := StringEqual(lastFrame,'lastFrame');
			if playToLast then
				begin
					if chapterMode then
						begin
							lastFrame := '99';
							lastFrameNum := 99;
						end
					else
						begin
							lastFrame := '54000';
							lastFrameNum := 54000;
						end;
				end
			else lastFrameNum := StrToLong(lastFrame);
			{ Set the speed. }
			fpsNum := StrToLong(speed);
			if fpsNum = 0 then fpsNum := 30;

			{ Figure out whether we're playing forward or reverse. }
			rev := (not playToLast) and
						(((not startHere) and (lastFrameNum < StrToLong(firstFrame))) or (lastFrameNum = 0));

			{ Adjust ending point if we're playing backward in chapter mode (since it stops as soon as it enters the chapter). }
			if rev and chapterMode and (lastFrameNum <> 0) then
				begin
					lastFrameNum := lastFrameNum-1;
					lastFrame := LongToStr(lastFrameNum);
				end;

			{ Go to the first frame. }
			if not startHere then search(firstFrame,blankSearch);
			if fpsNum < 30 then sendCmd(Concat('.',LongToStr(30 div fpsNum),'.ED'))
			else if fpsNum > 30 then sendCmd(Concat('.',LongToStr(fpsNum div 30),'.EC'))
			else sendCmd('.1.ED');
			{ Play it. }
			if rev then
				begin
					if lastFrameNum = 0 then sendCmd('FA')
					else sendCmd(Concat('.',lastFrame,'.FA'));
				end
			else
				begin
					if fpsNum = 30 then
						begin
							if (lastFrameNum <> 0) and (not playToLast) then sendCmd(Concat('.',lastFrame,'.F3'))
							else sendCmd('FD');
						end
					else if playToLast then sendCmd('F2')
					else sendCmd(Concat('.',lastFrame,'.F2'));
				end;
		end;

	function scan(scanForward, chapterMode: boolean): str255;
		{ Scan forward (if scanForward is true), or backward. }

		begin
			if scanForward then
				begin
					if chapterMode then sendCmd('.99.83')
					else sendCmd('.54000.83')
				end
			else sendCmd('.0.83');
			scan := 'only once';
		end;

	procedure step(goForward: boolean);
		{ Step one frame forward (if goForward is true) or backward. }

		begin
			if goForward then SendCmd('F6')
			else SendCmd('FE');
		end;

	function speeds: str255;
		{ Return the valid speeds for this player. }

		begin
			speeds := '1,2,3,5,6,10,15,20,30,60,90';
		end;

	function time: str255;
		{ Return the time for the current frame. }

		begin
			time := 'notImplemented';
		end;

	function version: str255;
		{ Return the version for this player driver. }

		begin
			version := 'P6000A 1.0';
		end;

	begin
		pCount := paramPtr^.paramCount;

		if pCount <= 0 then Fail('parameter count is not > 0');

		if pCount > 1 then GetStrParm(2,p1)
		else p1 := '';
		if pCount > 2 then GetStrParm(3,p2)
		else p2 := '';

		GetStrParm(1,str);

		returnValue := '';

		if StringEqual(str,'chapter') then returnValue := chapter
		else if StringEqual(str,'control') then
			begin
				for i := 2 to pCount do
					begin
						GetStrParm(i,str);
						control(str);
					end;
			end
		else if StringEqual(str,'extended') then returnValue := extended(p1)
		else if StringEqual(str,'fps') then fps(p1)
		else if StringEqual(str,'frame') then returnValue := frame
		else if StringEqual(str,'name') then returnValue := name
		else if StringEqual(str,'play') then
			begin
				GetStrGlobal('videoMode',str);
				inChapterMode := StringEqual(str,'chapterMode');
				GetStrGlobal('blankNextVideo',str);
				if str = '' then
					begin
						GetStrGlobal('videoSpeed',str);
						play(p1,p2,str,false,inChapterMode);
					end
				else
					begin
						GetStrGlobal('videoSpeed',str);
						play(p1,p2,str,true,inChapterMode);
					end;
			end
		else if StringEqual(str,'scan') then
			begin
				GetStrGlobal('videoMode',str);
				inChapterMode := StringEqual(str,'chapterMode');
				if length(p1) < 1 then returnValue := scan(true,inChapterMode)
				else returnValue := scan(not ((p1[1] = 'b') or (p1[1] = 'B')),inChapterMode);
			end
		else if StringEqual(str,'search') then
			begin
				GetStrGlobal('blankNextVideo',str);
				search(p1,str <> '');
			end
		else if StringEqual(str,'sendCmd') then sendCmd(p1)
		else if StringEqual(str,'step') then step(p1 = '1')
		else if StringEqual(str,'speeds') then returnValue := speeds
		else if StringEqual(str,'status') then returnValue := status
		else if StringEqual(str,'stop') then stop
		else if StringEqual(str,'time') then returnValue := time
		else if StringEqual(str,'version') then returnValue := version;

		{ Return the result (if any). }
		paramPtr^.returnValue := PasToZero(returnValue)
	end;

end.
